package com.junray.mqttmanager

import android.content.Context
import android.net.ConnectivityManager
import android.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.eclipse.paho.android.service.MqttAndroidClient
import org.eclipse.paho.client.mqttv3.IMqttActionListener
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken
import org.eclipse.paho.client.mqttv3.IMqttToken
import org.eclipse.paho.client.mqttv3.MqttCallback
import org.eclipse.paho.client.mqttv3.MqttConnectOptions
import org.eclipse.paho.client.mqttv3.MqttException
import org.eclipse.paho.client.mqttv3.MqttMessage

internal class MQTTManager {

    companion object {
        private var instance: MQTTManager? = null
        fun getInstance(): MQTTManager {
            synchronized(this) {
                if (instance == null) {
                    instance = MQTTManager()
                }
            }
            return instance!!
        }
    }

    private val TAG: String = "MQTTManager_LJM"
    private lateinit var context: Context
    private var client: MqttAndroidClient? = null

    private var serverUri: String? = null
    private var clientId: String? = null
    private var subscriptionTopic: String? = null
    private var subscriptionTopics = arrayOf<String>()
    private var sendTopic: String? = null
    private var userName: String? = null
    private var password: String? = null

    private var callback: MQTTCallback? = null

    private val options = MqttConnectOptions() // Mqtt连接选项
    private val QOS_LEVEL = 1 // Mqtt QOS等级

    private val RECONNECT_INTERVAL: Long = 10000 // 重连间隔时间，单位毫秒
    private val MAX_RECONNECT_ATTEMPTS: Int = 5 // 最大重连次数
    private var reconnectAttempts = 0

    fun init(
        context: Context,
        config: MQTTConfig
    ) {
        if (client?.isConnected == true) {
            Log.e(TAG, "MQTT is connected")
            return
        }
        this.context = context
        this.serverUri = config.serverUri
        this.clientId = config.clientId
        this.subscriptionTopic = config.subscriptionTopic
        this.subscriptionTopics = config.subscriptionTopics
        this.sendTopic = config.sendTopic
        this.userName = config.userName
        this.password = config.password

        options.isAutomaticReconnect = true
        options.isCleanSession = true
        options.userName = userName
        options.password = password?.toCharArray()
        options.connectionTimeout = 10 // 设置连接超时时间
        options.keepAliveInterval = 20 // 设置心跳时间
        client = MqttAndroidClient(context, serverUri, clientId)

        client?.setCallback(mqttCallback)

        connect()
    }

    private fun connect() {
        if (client?.isConnected == false && isConnectIsNormal()) {
            try {
                Log.d(TAG, "MQTT connect clientId $clientId")
                client?.connect(options, null, actionListener)
            } catch (ex: MqttException) {
                ex.printStackTrace()
            }
        }
    }

    private fun subscribeToTopic() {
        if (subscriptionTopic.isNullOrEmpty()) {
            Log.e(TAG, "subscriptionTopic is null")
            return
        }
        try {
            client?.subscribe(subscriptionTopic, QOS_LEVEL, null, subscribeListener)
        } catch (ex: MqttException) {
            ex.printStackTrace()
        }
    }

    private fun subscribeToTopics() {
        if (subscriptionTopics.isEmpty()) {
            Log.e(TAG, "subscriptionTopics is null")
            return
        }
        val qos = IntArray(subscriptionTopics.size)
        for (i in qos.indices) {
            qos[i] = QOS_LEVEL
        }
        try {
            client?.subscribe(subscriptionTopics, qos, null, subscribeListener)
        } catch (ex: MqttException) {
            ex.printStackTrace()
        }
    }

    fun setMQTTCallback(callback: MQTTCallback?) {
        this.callback = callback
    }

    fun sendMsg(msg: String) {
        sendMsg(sendTopic, msg)
    }

    fun sendMsg(topic: String?, msg: String) {
        if (topic.isNullOrEmpty()) {
            Log.e(TAG, "topic is null")
            return
        }
        if (client?.isConnected == false) {
            Log.e(TAG, "MQTT is not connected")
            return
        }
        try {
            client?.publish(topic, msg.toByteArray(), QOS_LEVEL, false)
        } catch (e: MqttException) {
            throw RuntimeException(e)
        }
    }

    private fun close() {
        try {
            client?.unregisterResources()
            client?.close()
        } catch (e: Exception) {
            Log.e(TAG, "destroy error = ${e.message}")
        }
    }

    //销毁client
    fun destroy() {
        close()
        retry?.cancel()
        client = null
    }

    /**
     * 判断网络是否连接
     */
    private fun isConnectIsNormal(): Boolean {
        val connMgr = context
            .getSystemService(Context.CONNECTIVITY_SERVICE)
                as ConnectivityManager
        val activeNetwork = connMgr.activeNetworkInfo
        return if (null != activeNetwork && activeNetwork.isConnected) {
            activeNetwork.type >= 0
        } else false
    }

    private val actionListener = object : IMqttActionListener {
        override fun onSuccess(asyncActionToken: IMqttToken) {
            callback?.onMQTTStatus(MQTTState.STATE_CONNECT_SUCCESS)
            subscribeToTopic()
            subscribeToTopics()
            reconnectAttempts = 0
        }

        override fun onFailure(asyncActionToken: IMqttToken, exception: Throwable) {
            callback?.onMQTTStatus(MQTTState.STATE_CONNECT_FAILURE)
        }
    }

    private val subscribeListener = object : IMqttActionListener {
        override fun onSuccess(asyncActionToken: IMqttToken) {
            callback?.onMQTTStatus(MQTTState.STATE_SUBSCRIBE_SUCCESS)
        }

        override fun onFailure(asyncActionToken: IMqttToken, exception: Throwable) {
            callback?.onMQTTStatus(MQTTState.STATE_SUBSCRIBE_FAILURE)
        }
    }

    private val retry = CoroutineScope(Job() + Dispatchers.IO)
    private val mqttCallback = object : MqttCallback {
        override fun connectionLost(cause: Throwable) { // MQTT自带重连机制，当前不需处理连接断开的情况
//            retry.cancel()
//            retry.launch {
//                while (client?.isConnected == false) {
//                    try {
//                        reconnectAttempts++ // 重连次数加1
//                        if (reconnectAttempts > MAX_RECONNECT_ATTEMPTS) {
//                            close()
//                            callback?.onMQTTStatus(MQTTState.STATE_CONNECTION_LOST)
//                            return@launch
//                        }
//                        connect() // 尝试重新连接
//                        delay(RECONNECT_INTERVAL) // 等待一段时间后重连
//                    } catch (e: InterruptedException) {
//                        Log.e(TAG, "connectionLost reconnect error = ${e.message}")
//                    }
//                }
//            }
        }

        override fun messageArrived(topic: String, message: MqttMessage) { // 处理接收到的消息
            ArrivedMsgFlow.send(ArrivedMessage(topic, String(message.payload)))
        }

        override fun deliveryComplete(token: IMqttDeliveryToken) { // 处理消息发送完成的情况
            callback?.onMQTTStatus(MQTTState.STATE_SEND_COMPLETE)
        }
    }

}